$OpenBSD: patch-tircproxy_c,v 1.3 2015/01/17 22:58:12 naddy Exp $
--- tircproxy.c.orig	Thu May  4 22:53:30 2000
+++ tircproxy.c	Sat Jan 17 23:54:08 2015
@@ -57,6 +57,7 @@ char *Version = 
 #include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <limits.h>
 
 #include <tircproxy.h>
 
@@ -119,6 +120,8 @@ char *Version = 
 #endif
 #undef TRANS
 
+#undef RANGEDPORTS
+
 #if IPF
 # if HAVE_NETINET_IP_NAT_H
 #  include <sys/ioctl.h>
@@ -142,6 +145,15 @@ char *Version = 
 # endif
 #endif
 
+#ifdef PF
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <net/pfvar.h>
+#define TRANS 1
+#define RANGEDPORTS 1
+#endif
+
 #ifndef LINUX
 # define LINUX 0
 #endif
@@ -187,7 +199,12 @@ int hosts_ctl(char *daemon, 
 
 #endif
 
+#ifdef RANGEDPORTS
+int min_port = IPPORT_HIFIRSTAUTO;
+int max_port = IPPORT_HILASTAUTO; 
+#endif
 
+
 /* Typedefs.
 */
 typedef unsigned long	ipaddr_t;
@@ -213,6 +230,10 @@ static uid_t get_user_id	(char *user);
 static uid_t get_group_id	(uid_t uid);
 static int bind_to_port		(ipaddr_t bind_ip, short bind_port, 
 				 int backlog, int dlev);
+#ifdef RANGEDPORTS
+static int bind_to_ranged_port  (ipaddr_t bind_ip, short min_port,
+				 short max_port, int backlog, int dlev);
+#endif
 static int connect_to_server	(struct sockaddr_in addr);
 static void lookup_hostname	(struct sockaddr_in *addr, char *hostname,
 				 int hostlen, int needed);
@@ -350,8 +371,14 @@ int main(int argc, char **argv)
 
 	/* Parse the command line arguments.
 	*/
-	while ((arg = getopt(argc, argv, "ab:d:h?i:o:pq:r:s:t:CDHIKLMNOQRSU")) != EOF)
+#ifdef RANGEDPORTS
+	while ((arg = getopt(argc, argv, "ab:d:h?i:m:o:pq:r:s:t:x:CDHIKLMNOQRSU")) != EOF)
 	{
+		char *p;
+#else
+        while ((arg = getopt(argc, argv, "ab:d:h?i:o:pq:r:s:t:CDHIKLMNOQRSU")) != EOF)
+	{
+#endif
 	   	switch (arg)
 		{
 
@@ -375,6 +402,15 @@ int main(int argc, char **argv)
 		case 'i':
 			visible_ip_i = get_ip_addr(optarg);
 			break;
+#ifdef RANGEDPORTS
+		case 'm':
+			min_port = strtol(optarg, &p, 10);
+			if (!*optarg || *p)
+				usage(argv[0], "Missing min port."); 
+			if (min_port < 0 || min_port > USHRT_MAX)
+				usage(argv[0], "Invalid min port.");
+			break;
+#endif		
 		case 'o':
 			visible_ip_o = get_ip_addr(optarg);
 			break;
@@ -408,6 +444,16 @@ int main(int argc, char **argv)
 		case 't':
 			throttle_seconds = atoi(optarg);
 			break;
+
+#ifdef RANGEDPORTS
+                case 'x':
+                        max_port = strtol(optarg, &p, 10);
+                        if (!*optarg || *p)
+                                usage(argv[0], "Missing max port.");
+                        if (max_port < 0 || max_port > USHRT_MAX)
+                                usage(argv[0], "Invalid max port.");
+                        break;
+#endif       
 		
 		case 'C':
 			allow_dcc_chat = 0;
@@ -467,6 +513,11 @@ int main(int argc, char **argv)
 			break;
 		}
 	}
+
+#ifdef RANGEDPORTS
+	if (max_port < min_port)
+		usage(argv[0], "max port can not be smaller than min port.");
+#endif
    
    	/* Set a few variables to 'default' values.
 	*/
@@ -684,7 +735,12 @@ Options:\n",Version,prog);
    -p          Require a valid Unix password for access.\n\
    -q file     Read a list of 'quizzes' from the named file.\n");
 #endif
+#ifdef RANGEDPORTS
 	fprintf(stderr,"\
+   -m minport  Specify the minimum port to bind incoming DCC connects to.\n\
+   -x maxport  Specify the maxmium port to bind incoming DCC connects to.\n");
+#endif
+	fprintf(stderr,"\
    -a          Anonymous mode, hide as much info about the user as possible.\n\
    -r user     Run as the specified user in server mode.\n\
    -t n	       Force a sleep(1) between connections under n seconds apart.\n\
@@ -853,58 +909,146 @@ static uid_t get_group_id(uid_t uid)
 	return (gid);
 }
 
-
 /* Bind to the specified ip and port.
 */
 static int bind_to_port(ipaddr_t bind_ip, short bind_port, int backlog, int dlev)
 {
+        struct sockaddr_in      addr;
+        int                     sock;
+
+        /* Allocate a socket.
+        */
+        if ((sock = socket(AF_INET, SOCK_STREAM,
+                        getprotobyname("tcp")->p_proto)) < 0)
+        {
+                debug_msg(dlev, LOG_WARNING, "socket(): %d - %.256s", errno, strerror(errno));
+                return(-1);
+        }
+
+#ifdef TIRC_DEBUG
+        /* Set the SO_REUSEADDR option for debugging.
+        */
+        if (debug_level >= DEBUG_NOFORK) {
+                int on = 1;
+
+                setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
+        }
+#endif
+
+        /* Set the address to listen to.
+        */
+        memset(&addr, '\0', sizeof(addr));
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(bind_port);   
+        addr.sin_addr.s_addr = bind_ip;
+
+        /* Bind our socket to the above address.
+        */
+        if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+        {
+                debug_msg(dlev, LOG_WARNING, "bind(): %d - %.256s", errno, strerror(errno));
+                return(-1);
+        }
+
+        /* Establish a large listen backlog.
+        */
+        if (listen(sock, backlog) < 0)
+        {
+                debug_msg(dlev, LOG_WARNING, "listen(): %d - %.256s", errno, strerror(errno));
+                return(-1);
+        }
+
+        return (sock);
+}             
+
+#ifdef RANGEDPORTS
+/* Bind to the specified ip and choose a port in the range of min_port and max_port.
+*/
+static int bind_to_ranged_port(ipaddr_t bind_ip, short min_port, short max_port, int backlog, int dlev)
+
+{
 	struct sockaddr_in	addr;
 	int			sock;
 
-	/* Allocate a socket.
-	*/
-	if ((sock = socket(AF_INET, SOCK_STREAM, 
-			getprotobyname("tcp")->p_proto)) < 0)
+	int 			direction = 1;
+	int 			count;
+	short			start_port;
+
+	if (min_port > max_port)
 	{
-     		debug_msg(dlev, LOG_WARNING, "socket(): %d - %.256s", errno, strerror(errno));
-	   	return(-1);
+                debug_msg(dlev, LOG_WARNING, "min_port > max_port");
+                return(-1); 
 	}
+	
+	count = 1 + max_port - min_port;
+	
+	start_port = (arc4random() % count) + min_port;
 
+	while (count-- > 0) {
+	
+		/* Allocate a socket.
+		*/
+		if ((sock = socket(AF_INET, SOCK_STREAM, 
+			getprotobyname("tcp")->p_proto)) < 0)
+		{
+     			debug_msg(dlev, LOG_WARNING, "socket(): %d - %.256s", errno, strerror(errno));
+	   		return(-1);
+		}
+
 #ifdef TIRC_DEBUG
-	/* Set the SO_REUSEADDR option for debugging.
-	*/
-	if (debug_level >= DEBUG_NOFORK) {
-	 	int on = 1;
+		/* Set the SO_REUSEADDR option for debugging.
+		*/
+		if (debug_level >= DEBUG_NOFORK) {
+		 	int on = 1;
 
-		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
-	}
+			setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
+		}	
 #endif
 
-	/* Set the address to listen to.
-	*/
-   	memset(&addr, '\0', sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(bind_port);
-	addr.sin_addr.s_addr = bind_ip;
+		/* Set the address to listen to.
+		*/
+   		memset(&addr, '\0', sizeof(addr));
+		addr.sin_family = AF_INET;
+		addr.sin_port = htons(start_port);
+		addr.sin_addr.s_addr = bind_ip;
 
-	/* Bind our socket to the above address.
-	*/
-	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-	{
-		debug_msg(dlev, LOG_WARNING, "bind(): %d - %.256s", errno, strerror(errno));
-	   	return(-1);
-	}
+		/* Bind our socket to the above address.
+		*/
+		if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+		{
+			if (errno != EADDRINUSE)
+			{
+	                        debug_msg(dlev, LOG_WARNING, "bind(): %d - %.256s", errno, strerror(errno));	
+				close(sock);
+				return(-1);
+			}			 	
+			/* It's in use, try the next port */
+			close(sock);
 
-	/* Establish a large listen backlog.
-	*/
-	if (listen(sock, backlog) < 0)
-	{
-		debug_msg(dlev, LOG_WARNING, "listen(): %d - %.256s", errno, strerror(errno));
-	   	return(-1);
-	}
+			start_port += direction;
 
-	return (sock);
+			if (start_port < min_port)
+				start_port = max_port;
+			else if (start_port > max_port)
+				start_port = min_port; 
+			continue;
+		} 
+
+		/* Establish a large listen backlog.
+		*/
+		if (listen(sock, backlog) < 0)
+		{
+			debug_msg(dlev, LOG_WARNING, "listen(): %d - %.256s", errno, strerror(errno));	
+			close(sock);
+	   		return(-1);
+		}
+
+		return (sock);
+	}
+	errno = EAGAIN;
+	return(-1);
 }
+#endif
 
 
 /* Connect to the a server.
@@ -1070,6 +1214,11 @@ static void trans_proxy(int sock, struct sockaddr_in *
 	natlookup_t     			natlook;
 	int					fd;
 #endif
+#ifdef PF
+	struct sockaddr_in			ext, gwy;
+	struct pfioc_natlook			natlook;
+	int					fd;
+#endif
    
    	/* Give this thing 10 minutes to get started (paranoia).
 	*/
@@ -1166,6 +1315,50 @@ static void trans_proxy(int sock, struct sockaddr_in *
                 to_addr.sin_family = AF_INET;
                 to_addr.sin_port = htons(ntohs(natlook.nl_realport));
                 to_addr.sin_addr.s_addr = get_ip_addr(inet_ntoa(natlook.nl_realip));
+# else
+#  ifdef PF
+                to_len = sizeof(ext);
+                if (getpeername(sock, (struct sockaddr *)&ext, &to_len) == -1)
+	     	{
+                	perror("getpeername");
+                	exit(-1);
+                }
+
+                to_len = sizeof(gwy);
+                if (getsockname(sock, (struct sockaddr *)&gwy, &to_len) == -1) 
+	     	{
+                	perror("getsockname");
+                	exit(-1);
+                }
+
+		if ((fd = open("/dev/pf", O_RDWR)) == -1) {
+			perror("open(\"/dev/pf\")");
+			exit(-1);
+		}
+
+		memset(&natlook, 0, sizeof(struct pfioc_natlook));
+		natlook.af		= AF_INET;
+		natlook.proto		= IPPROTO_TCP;
+		natlook.direction	= PF_OUT;	/* s = ext, d = gwy, r = svr */
+		natlook.saddr.v4.s_addr	= ext.sin_addr.s_addr;
+		natlook.sport		= ext.sin_port;
+		natlook.daddr.v4.s_addr	= gwy.sin_addr.s_addr;
+		natlook.dport		= gwy.sin_port;
+
+                if (ioctl(fd, DIOCNATLOOK, &natlook) == -1)
+	     	{
+                	perror("ioctl");
+			close(fd);
+                	exit(-1);
+                }
+
+                close(fd);
+ 
+		memset(&to_addr, 0, sizeof(to_addr));
+		to_addr.sin_family	= AF_INET;
+		to_addr.sin_port	= natlook.rdport;
+		to_addr.sin_addr.s_addr	= natlook.rdaddr.v4.s_addr;
+#  endif /* PF */
 # endif /* IFP */
 #endif /* LINUX */
 	}
@@ -2047,9 +2240,15 @@ static char *proxy_dcc(int destaddr, int destport, int
 	** 	(re: BUGTRAQ, 1998, December 22 & 23)
 	*/
 	len = 10;
-	while ((listen_sock = bind_to_port((incoming) ? visible_ip_o : visible_ip_i, 
-					   ((rand() + getpid()) % PPOOL) + 1025, 
+#ifdef RANGEDPORTS
+	while ((listen_sock = bind_to_ranged_port((incoming) ? visible_ip_o : visible_ip_i, 
+					   min_port, max_port, 
 					   1, DEBUG_TRIVIA)) < 0) 
+#else
+        while ((listen_sock = bind_to_port((incoming) ? visible_ip_o : visible_ip_i,
+                                           ((rand() + getpid()) % PPOOL) + 1025, 
+                                           1, DEBUG_TRIVIA)) < 0)  
+#endif
      	{
 	   	if (--len < 1) {
 		   	debug_msg(0, LOG_ERR, "Failed to bind to port, dropping DCC!");
